Skip to content

feat: integrate Didit identity verification for KYC compliance#440

Merged
0xdevcollins merged 3 commits into
boundlessfi:mainfrom
Ekene001:feat/Integrate-Didit-identity-verification-(KYC)-into-the-application
Mar 4, 2026
Merged

feat: integrate Didit identity verification for KYC compliance#440
0xdevcollins merged 3 commits into
boundlessfi:mainfrom
Ekene001:feat/Integrate-Didit-identity-verification-(KYC)-into-the-application

Conversation

@Ekene001

@Ekene001 Ekene001 commented Mar 4, 2026

Copy link
Copy Markdown
Contributor

closes #439

This PR adds end-to-end Didit identity verification support to the app, including session creation, verification launch flow, and identity status display in user settings/profile surfaces.

What changed

  • Added Didit verification session API client and typed response mapping.
  • Added reusable verification UI:
  • DiditVerifyButton to start verification with @didit-protocol/sdk-web.
  • IdentityVerificationSection to show verification state (Approved, Declined, In Review) and actions.
  • Extended user types to include:
  • identityVerificationStatus
  • identityVerificationAt
  • Updated settings UX to include a new Identity tab and refresh user data after verification completion.
  • Updated user menu display to reflect verification state visually.
  • Added @didit-protocol/sdk-web dependency and verification badge asset.

Impact

  • Enables KYC identity verification flow directly in-app.
  • Improves compliance readiness with visible verification status and completion metadata.

Testing

  • Manually verified UI flow for identity tab rendering, verification launch, and status display updates.
  • Confirmed API integration handles invalid session payloads with user-facing error state.

Summary by CodeRabbit

  • New Features

    • Added identity verification flow and "Verify identity" action in Settings.
    • New Identity tab in Settings showing verification status (Approved / Declined / In Review) and completion date.
    • Verification badge shown in user menu/profile when verified.
  • Improvements

    • User profile refreshes automatically after verification and cache is invalidated to reflect updated status.

@Ekene001
Ekene001 requested a review from 0xdevcollins as a code owner March 4, 2026 16:02
@vercel

vercel Bot commented Mar 4, 2026

Copy link
Copy Markdown

@Ekene001 is attempting to deploy a commit to the Threadflow Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai

coderabbitai Bot commented Mar 4, 2026

Copy link
Copy Markdown

Warning

Rate limit exceeded

@Ekene001 has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 5 minutes and 42 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 3f6ee02a-cf4d-425d-bad5-cfab706b1aa9

📥 Commits

Reviewing files that changed from the base of the PR and between f91b882 and bec42ee.

📒 Files selected for processing (3)
  • hooks/use-auth.ts
  • lib/api/types.ts
  • types/user.ts
📝 Walkthrough

Walkthrough

Adds Didit identity verification: API client, UI components (verify button + Identity section), settings tab wiring, user type fields, auth profile cache invalidation on verification, and a runtime dependency on the Didit web SDK.

Changes

Cohort / File(s) Summary
Settings UI
app/me/settings/SettingsContent.tsx
Added "Identity" tab, a useCallback fetchUserData hook, and handleVerificationComplete to refresh user data and invalidate auth profile cache after verification.
Didit API
lib/api/didit.ts
New API module createDiditSession(params?) that POSTs to /didit/create-session, unwraps data, validates presence of session_token and url, and returns normalized DiditCreateSessionResponse with verification_url.
Didit UI Components
components/didit/DiditVerifyButton.tsx, components/didit/IdentityVerificationSection.tsx
New client components: DiditVerifyButton (calls createDiditSession, dynamically loads @didit-protocol/sdk-web, starts SDK modal, handles onComplete/onError/onCancel) and IdentityVerificationSection (shows status badge, verified date, renders verify button, fires onVerificationComplete). Exports new types/interfaces for the section.
User types
lib/api/types.ts, types/user.ts
Added optional `identityVerificationStatus?: 'Approved'
User Menu UI
components/user/UserMenu.tsx
Added verified badge/icon next to avatar and username when identityVerificationStatus === 'Approved'.
Auth cache invalidation
hooks/use-auth.ts
Added invalidateAuthProfileCache() and an invalidation signal consumed by useAuth/useAuthStatus to force profile refetch on external invalidation.
Dependencies
package.json
Added runtime dependency @didit-protocol/sdk-web ^0.1.8.

Sequence Diagram

sequenceDiagram
    actor User
    participant Frontend as Frontend (React)
    participant Backend as Backend API
    participant SDK as Didit SDK (`@didit-protocol/sdk-web`)
    participant Didit as Didit Service

    User->>Frontend: Click "Verify identity"
    Frontend->>Backend: POST /api/didit/create-session
    Backend-->>Frontend: Return session_token & url (mapped -> verification_url)
    Frontend->>SDK: import & initialize DiditSdk.shared
    Frontend->>SDK: set onComplete callback
    Frontend->>SDK: startVerification({ url: verification_url })
    SDK->>Didit: Open verification modal / communicate with service
    User->>Didit: Complete / Cancel / Fail verification
    Didit->>SDK: Emit completion result
    SDK->>Frontend: Invoke onComplete callback
    Frontend->>Frontend: onSuccess -> call onVerificationComplete -> invalidate cache
    Frontend->>Backend: GET /users/me (refetch)
    Backend-->>Frontend: Return updated user with identityVerificationStatus
    Frontend->>Frontend: Update UI (badge/date)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Suggested reviewers

  • 0xdevcollins
  • Benjtalkshow

Poem

🐰 I hopped to add a verification gleam,

A button, badge, and Didit dream,
Sessions start and modals play,
Users verified, hip‑hip‑hooray,
A little hop for trust today ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: integrate Didit identity verification for KYC compliance' accurately and clearly summarizes the main objective of the changeset—adding Didit identity verification.
Linked Issues check ✅ Passed All primary objectives from issue #439 are implemented: Didit session API client, Identity tab with verification section, User types extended with verification fields, SDK integration with onComplete handlers, and cache invalidation for data refresh.
Out of Scope Changes check ✅ Passed All changes are directly related to Didit identity verification implementation. No unrelated modifications detected outside the scope of KYC compliance integration.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (3)
lib/api/types.ts (1)

69-71: Extract a shared IdentityVerificationStatus type to avoid drift.

The status union is now duplicated in lib/api/types.ts and types/user.ts; centralizing it in one exported alias will keep both user models consistent over time.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/api/types.ts` around lines 69 - 71, Create a shared exported type alias
named IdentityVerificationStatus (e.g., export type IdentityVerificationStatus =
'Approved' | 'Declined' | 'In Review' | null) and replace the inline union used
by identityVerificationStatus in lib/api/types.ts (and the duplicate in
types/user.ts) with this single exported alias so both modules import and
reference the same centrally defined type to prevent drift; update
imports/exports accordingly.
components/didit/IdentityVerificationSection.tsx (1)

54-57: Use a typed const-arrow component declaration for consistency.

Convert this exported function declaration to a const-arrow component with an explicit type annotation to match repo conventions.

As per coding guidelines, **/*.{ts,tsx}: Prefer const arrow functions with explicit type annotations over function declarations.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/didit/IdentityVerificationSection.tsx` around lines 54 - 57,
Convert the exported function declaration IdentityVerificationSection to a
const-arrow component with an explicit type annotation: replace "export function
IdentityVerificationSection({ user, onVerificationComplete }:
IdentityVerificationSectionProps) { ... }" with a const declaration like "export
const IdentityVerificationSection: React.FC<IdentityVerificationSectionProps> =
({ user, onVerificationComplete }) => { ... }", keeping the same props usage and
body (including the IdentityVerificationSectionProps type), and ensure any
default exports or named exports remain unchanged.
components/didit/DiditVerifyButton.tsx (1)

18-25: Prefer a typed const-arrow component export here.

This should follow the TS/TSX convention used in your guidelines to keep component declarations consistent.

As per coding guidelines, **/*.{ts,tsx}: Prefer const arrow functions with explicit type annotations over function declarations.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/didit/DiditVerifyButton.tsx` around lines 18 - 25, Replace the
current function declaration for DiditVerifyButton with a typed const-arrow
component: export a const named DiditVerifyButton typed as
React.FC<DiditVerifyButtonProps> (or
React.FunctionComponent<DiditVerifyButtonProps>) and implement the component as
a const arrow (const DiditVerifyButton: React.FC<DiditVerifyButtonProps> = ({
onSuccess, onError, onCancel, className, disabled = false, userId }) => { ...
}), preserving the same prop names and default for disabled and keeping existing
logic and export.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app/me/settings/SettingsContent.tsx`:
- Around line 109-112: The verification callback currently only calls
fetchUserData (updating SettingsContent local state) so the global cached
profile used by useAuthStatus (and read by UserMenu.tsx) stays stale; update the
onVerificationComplete handler to also invalidate or refresh the global
auth/profile cache (e.g., call the auth context updater or a revalidation
function such as refreshAuth / invalidateProfileCache / revalidateAuth) so both
IdentityVerificationSection and useAuthStatus see the new verification state —
change the handler passed to IdentityVerificationSection to call fetchUserData
and the global refresh/invalidate function (or dispatch the auth/profile update
via the auth context) so the UserMenu badge updates immediately.

In `@components/didit/DiditVerifyButton.tsx`:
- Around line 63-68: The catch block in DiditVerifyButton (the try/catch that
currently does setError(message) and setLoading(false)) swallows startup
exceptions and never notifies the parent; update that catch to also call the
provided onError handler (e.g., props.onError or onError) with the original
Error (or the derived message) so the parent receives the failure, then preserve
the existing local state updates (setError and setLoading(false)); ensure you
pass the actual Error when available (e instanceof Error ? e : new
Error(message)) to keep rich error info.

In `@components/user/UserMenu.tsx`:
- Around line 73-83: The verified-badge condition is inverted: the avatar badge
block using isVerified currently renders when the user is NOT verified; in the
UserMenu component update the conditional so the <span> with the verified
<Image> (the block containing src='/verified.png' and alt='Verified') only
renders when isVerified is true (i.e., change the {!isVerified && ...} check to
isVerified && ...), leaving the markup and image props unchanged.

---

Nitpick comments:
In `@components/didit/DiditVerifyButton.tsx`:
- Around line 18-25: Replace the current function declaration for
DiditVerifyButton with a typed const-arrow component: export a const named
DiditVerifyButton typed as React.FC<DiditVerifyButtonProps> (or
React.FunctionComponent<DiditVerifyButtonProps>) and implement the component as
a const arrow (const DiditVerifyButton: React.FC<DiditVerifyButtonProps> = ({
onSuccess, onError, onCancel, className, disabled = false, userId }) => { ...
}), preserving the same prop names and default for disabled and keeping existing
logic and export.

In `@components/didit/IdentityVerificationSection.tsx`:
- Around line 54-57: Convert the exported function declaration
IdentityVerificationSection to a const-arrow component with an explicit type
annotation: replace "export function IdentityVerificationSection({ user,
onVerificationComplete }: IdentityVerificationSectionProps) { ... }" with a
const declaration like "export const IdentityVerificationSection:
React.FC<IdentityVerificationSectionProps> = ({ user, onVerificationComplete })
=> { ... }", keeping the same props usage and body (including the
IdentityVerificationSectionProps type), and ensure any default exports or named
exports remain unchanged.

In `@lib/api/types.ts`:
- Around line 69-71: Create a shared exported type alias named
IdentityVerificationStatus (e.g., export type IdentityVerificationStatus =
'Approved' | 'Declined' | 'In Review' | null) and replace the inline union used
by identityVerificationStatus in lib/api/types.ts (and the duplicate in
types/user.ts) with this single exported alias so both modules import and
reference the same centrally defined type to prevent drift; update
imports/exports accordingly.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 94633cb5-2447-4094-a6ec-031930abfa54

📥 Commits

Reviewing files that changed from the base of the PR and between 5eda51b and af80da9.

⛔ Files ignored due to path filters (2)
  • package-lock.json is excluded by !**/package-lock.json
  • public/verified.png is excluded by !**/*.png
📒 Files selected for processing (8)
  • app/me/settings/SettingsContent.tsx
  • components/didit/DiditVerifyButton.tsx
  • components/didit/IdentityVerificationSection.tsx
  • components/user/UserMenu.tsx
  • lib/api/didit.ts
  • lib/api/types.ts
  • package.json
  • types/user.ts

Comment thread app/me/settings/SettingsContent.tsx
Comment thread components/didit/DiditVerifyButton.tsx
Comment thread components/user/UserMenu.tsx Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
hooks/use-auth.ts (1)

167-170: ⚠️ Potential issue | 🟡 Minor

Add missing router dependency to logout callback.

Line 167-170 has an empty dependency array while logout uses router obtained from useRouter(). Include router in the dependency array to comply with the react-hooks/exhaustive-deps ESLint rule.

Proposed patch
-  }, []);
+  }, [router]);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@hooks/use-auth.ts` around lines 167 - 170, The logout useCallback currently
omits router from its dependency array causing an ESLint hook dependency
violation; update the useCallback declaration that defines logout (which calls
authClient.signOut() and router.push('/')) to include router in the dependency
array (e.g., [router]) so the callback properly captures the useRouter() value
and satisfies react-hooks/exhaustive-deps.
🧹 Nitpick comments (1)
hooks/use-auth.ts (1)

9-28: Prefer typed const-arrow functions for new helpers.

The newly added helpers use function declarations; in this codebase, these should be const arrow functions with explicit type annotations.

Proposed refactor
-export function invalidateAuthProfileCache() {
+export const invalidateAuthProfileCache: () => void = () => {
   authProfileInvalidationListeners.forEach(listener => {
     listener();
   });
-}
+};

-function useAuthProfileInvalidationSignal() {
+const useAuthProfileInvalidationSignal: () => number = () => {
   const [refreshSignal, setRefreshSignal] = useState(0);

   useEffect(() => {
@@
   }, []);

   return refreshSignal;
-}
+};

As per coding guidelines, "Prefer const arrow functions with explicit type annotations over function declarations".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@hooks/use-auth.ts` around lines 9 - 28, The two function declarations should
be converted to const arrow functions with explicit TypeScript types: change
`export function invalidateAuthProfileCache()` to `export const
invalidateAuthProfileCache: () => void = () => { ... }` and change `function
useAuthProfileInvalidationSignal()` to `const useAuthProfileInvalidationSignal:
() => number = () => { ... }`; preserve the implementation (state, useEffect,
listener add/delete) and keep the export on `invalidateAuthProfileCache`.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@hooks/use-auth.ts`:
- Line 64: Remove the "!profileLoading" check from the conditional that gates
profile refetches in both useAuth() and useAuthStatus(): find the if that
currently reads something like if (session && 'user' in session && session.user
&& !profileLoading) inside useAuth() and the analogous check inside
useAuthStatus(), and change them to only verify session presence (e.g., if
(session && 'user' in session && session.user)), so profileRefreshSignal changes
are not ignored while a load is in progress and invalidation signals can replay
after loading finishes.
- Line 10: The forEach call uses an expression-bodied arrow
(authProfileInvalidationListeners.forEach(listener => listener());) which can
trigger the lint rule about implicit returns; change the callback to a
block-bodied arrow so it does not implicitly return a value — e.g., update the
forEach invocation on authProfileInvalidationListeners to use (listener) => {
listener(); } so the listener() call is executed but no expression is returned.

---

Outside diff comments:
In `@hooks/use-auth.ts`:
- Around line 167-170: The logout useCallback currently omits router from its
dependency array causing an ESLint hook dependency violation; update the
useCallback declaration that defines logout (which calls authClient.signOut()
and router.push('/')) to include router in the dependency array (e.g., [router])
so the callback properly captures the useRouter() value and satisfies
react-hooks/exhaustive-deps.

---

Nitpick comments:
In `@hooks/use-auth.ts`:
- Around line 9-28: The two function declarations should be converted to const
arrow functions with explicit TypeScript types: change `export function
invalidateAuthProfileCache()` to `export const invalidateAuthProfileCache: () =>
void = () => { ... }` and change `function useAuthProfileInvalidationSignal()`
to `const useAuthProfileInvalidationSignal: () => number = () => { ... }`;
preserve the implementation (state, useEffect, listener add/delete) and keep the
export on `invalidateAuthProfileCache`.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c21707ba-0e43-4ad0-b1cb-8cae684d5204

📥 Commits

Reviewing files that changed from the base of the PR and between af80da9 and f91b882.

📒 Files selected for processing (4)
  • app/me/settings/SettingsContent.tsx
  • components/didit/DiditVerifyButton.tsx
  • components/user/UserMenu.tsx
  • hooks/use-auth.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • app/me/settings/SettingsContent.tsx
  • components/didit/DiditVerifyButton.tsx
  • components/user/UserMenu.tsx

Comment thread hooks/use-auth.ts Outdated
Comment thread hooks/use-auth.ts Outdated
@0xdevcollins
0xdevcollins merged commit b56cb43 into boundlessfi:main Mar 4, 2026
6 of 7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Integrate Didit identity verification (KYC) into the application

2 participants